<workarea>
    <style>
        #work-canvas {
            position: absolute;
            z-index: 1;
        }

        #canvas-container {
            height: calc(100vh - 190px);
            display: block;
            overflow: auto;
            position: relative;
        }
    </style>

    <div id="canvas-container">
        <img id="img" src={ imgSelected.src } width="{ imgSelected.size.width }" height="{ imgSelected.size.height }" />
        <div id="work-canvas" width="{ imgSelected.size.width }" height="{ imgSelected.size.height }"></div>
        <trackinglines></trackinglines>
    </div>

    <script>
        var self = this;

        $(document).on('click', function(event) {
            deselectAll();
            selectedElements = [];
        });

        $(document).keyup(function(e) {
            if (e.keyCode == 46) {//del key
                //Delete selected shape(s) with points
                //Delete selected points
                selectedElements.forEach(el => {
                    /* $("[for="+ el.node.id+"]").remove(); */
                    el.selectize(false, {
                        deepSelect: true
                    }) //unselect first
                    if (el.typ === 'point') {
                        detachPoint(el.attr("for"), el.node.id); //delete it from store
                        el.remove(); //remove parent svg node
                        eventBus.trigger('removeLabelPanelTag');
                    } else {
                        detachShape(el.node.id); //delete it from store
                        el.parent().remove(); //remove parent svg node
                        eventBus.trigger('removeLabelPanelTag')
                    }
                });

                selectedElements = [];
                //delete all the points related to a polygon
                //show snakebar to inform
            } else if (e.key === 'a' && e.altKey) {//TODO change this
                //Select all the labels
                selectAll();
                e.preventDefault();
                e.stopPropagation();
            } else if (e.key === 'c' && e.ctrlKey) {
                /**
                 * Save selected shape and feature point data into copiedElements
                 * schema looks like this:
                 * {
                 *   type,   // type of shape
                 *   rbox,   // the current rbox
                 *   points, // geometry of shape; @see getPoints
                 *   featurePoints: [   // array of featurePoints
                 *     {'type', 'rbox'} // featurePoint metaData
                 *   ]
                 * }
                 * Allows for copy pasting in other images
                 */
                copiedElements = [];
                selectedElements.forEach(shape => {
                    if (shape.hasClass('shape')) {
                        var featurePoints = [];
                        shape.siblings()
                            .filter(point => point.typ == 'point')
                            .forEach(point => {
                                featurePoints.push({
                                  'type': point.typ,
                                  'rbox': point.rbox(myCanvas)
                                });
                            });

                        copiedElements.push({
                          'type': shape.type,
                          'rbox': shape.rbox(myCanvas),
                          'points': getPoints(shape),
                          'featurePoints': featurePoints
                        });
                    }
                })
                e.preventDefault();
                e.stopPropagation();
            } else if (e.key === 'v' && e.ctrlKey) {
                /**
                 * loop through copiedElements and push new shapes in currently selected image
                 * use the following format for new IDs:
                 * shapeId = shapetype + shapeIndex
                 * featurePointId = shapetype + shapeIndex + point + pointIndex
                 */
                var currentImgData = labellingData[imgSelected.name];
                if (copiedElements) {
                    copiedElements.forEach(shape => {
                        // Add shape data and any points into labellingData
                        var shapeId = shape.type + currentImgData.shapeIndex++;
                        var shapeData = attachShapeToImg(shapeId, shape.type, shape.rbox, shape.points);
                        shape.featurePoints.forEach(point => {
                            var pointId = shapeId + point.type + currentImgData.pointIndex++;
                            attachPointToShape(shapeId, pointId, point.rbox);
                        });
                    });
                }
                // Call update to redraw shapes
                self.update();
                e.preventDefault();
                e.stopPropagation();
            } else if (e.key === 'ArrowLeft' && e.ctrlKey) {
                selectedElements.forEach(el => {
                    el.parent().dx(-1);
                });
                e.preventDefault();
                e.stopPropagation();
            } else if (e.key === 'ArrowRight' && e.ctrlKey) {
                selectedElements.forEach(el => {
                    el.parent().dx(1);
                });
                e.preventDefault();
                e.stopPropagation();
            } else if (e.key === 'ArrowUp' && e.ctrlKey) {
                selectedElements.forEach(el => {
                    el.parent().dy(-1);
                });
                e.preventDefault();
                e.stopPropagation();
            } else if (e.key === 'ArrowDown' && e.ctrlKey) {
                selectedElements.forEach(el => {
                    el.parent().dy(1);
                });
                e.preventDefault();
                e.stopPropagation();
            }
        });

        /**
         * Updates workarea when self.update() is called or when workarea changes
         */
        this.on('update', function() {
            myCanvas.clear();
            drawOnCanvas();
        })

        /**
         * Removes listeners when unmounting tag to prevent multiple instances of keyup and click from firing
         */
        this.on('unmount', function() {
            $(document).off('keyup');
            $(document).off('click');
            eventBus.off('removeWorkAreaFeaturePoint');
        });

        this.on('mount', function() {
            $("#canvas-container img").css("opacity", appConfig.imageOpacity || 1)
            .css({"width": imgSelected.size.scaledWidth, "height": imgSelected.size.scaledHeight});

            // this is needed so that everytime workarea remounts the scale are correct
            $("#work-canvas").attr({"width": imgSelected.size.scaledWidth, "height": imgSelected.size.scaledHeight});

            myCanvas = new SVG('work-canvas').size(imgSelected.size.scaledWidth, imgSelected.size.scaledHeight)

            drawOnCanvas();

            myCanvas.on('mousedown', function(event) {
                deselectAll();

                if (selectedTool && selectedTool.type !== "point" && !alreadyDrawing && selectedTool.drawable) {
                    var currentTool = selectedTool.create(event, myCanvas);
                    moveOnlyOnMoveTool(currentTool);
                    attachShapeListener(currentTool);

                    if (currentTool.type !== 'polygon') currentTool.draw(event);
                    selectedElement = currentTool;
                }
            });
            myCanvas.on('mouseup', function(event) {
                if (selectedTool && selectedElement) selectedElement.draw(event);
            });

            // Listen to label point removal
            eventBus.on('removeWorkAreaFeaturePoint', () => {
                self.update();
            })

            // Make sure label panel is initially hidden/empty
            eventBus.trigger('unmountLabelPanel');

            // Listen to label point selection
            eventBus.on('selectFeaturePoint', (f_point_id, parent_id) => {
                let el = SVG.get(f_point_id);
                deselectAll();
                riot.mount('label-panel', { id : parent_id, pointId : f_point_id })
                el.selectize({
                    rotationPoint: false,
                    points: []
                });
                selectedElements.push(el);
            })
        });

        /**
         * Attaches draw/resize listeners to shapes
         * @param {SVGElement} shape - SVGElement to attach draw/resize listeners
         */
        function attachShapeListener(shape) {
            shape.on('drawstart', function() {
                alreadyDrawing = true;
            });

            shape.on('drawcancel', function() {});

            shape.on('resizedone', function() {
                updateShapeDetailInStore(shape.node.id, shape.rbox(myCanvas),
                getPoints(shape));
            });

            shape.on('drawstop', function() {
                alreadyDrawing = false;
                if (!selectedTool.validate(shape)) { //Don't draw an element on accidentle click
                    shape.parent().remove();
                    shape.remove();
                } else {
                    attachShapeData(shape);
                    attachEvents(shape);
                }
            });
        }

        function attachEvents(currentTool) {
            moveOnlyOnMoveTool(currentTool);

            onMouse(currentTool.parent(), function(e) { //drag callback : doesn't fire on click
                if (currentTool.node.id === e.target.id) {
                    updateShapeDetailInStore(currentTool.node.id, currentTool.rbox(myCanvas), getPoints(
                        currentTool));
                    updateFeaturePoints(currentTool);
                }
            });
            currentTool.parent().on('click', function(e) {
                    if (selectedTool && selectedTool.type === "point") {
                        /**
                         * The position of currentTool is relative to its parent <svg> tag
                         * The position of point is also relative to the parent <svg> tag
                         * Point needs to be relative to the canvas
                         * so that its position is correct when redrawn
                         */
                        var point = getPointToDraw(e, currentTool, myCanvas.node.getBoundingClientRect());
                        attachPointToShape(currentTool.node.id, point.node.id, point.rbox(myCanvas));//ina store
                        attachEventsToFeaturePoint(point, currentTool);
                    } else if (e.altKey) { //deep select. Helpful in case of polygon
                        deselectAll();
                        currentTool.selectize({
                            rotationPoint: false,
                            deepSelect: true
                        });
                        selectedElements.push(currentTool);
                    } else {
                        if (!e.ctrlKey) { //deselect rest selected elements
                            deselectAll();
                            riot.mount('label-panel', { id : currentTool.node.id })
                        }
                        //select currnt element
                        currentTool.selectize({
                            rotationPoint: false
                        });
                        selectedElements.push(currentTool);
                    }
                    e.stopPropagation();
                } //click call back
            );
        }

        function moveOnlyOnMoveTool(el) {
            el.on('mousedown', function(e) {
                if (!selectedTool || selectedTool.type !== "move") {
                    e.preventDefault();
                    e.stopPropagation();
                }
            });
        }

        function attachEventsToFeaturePoint(f_point, parent) {
            f_point.typ = 'point';
            f_point.attr({
                for: parent.node.id
            })

            onMouse(f_point, function(e) { //drag callback: doesn't fire on click
                updateFeaturePointPosition(f_point);
            });

            f_point.on('click', function(e) {
                if (!e.ctrlKey) { //deselect rest selected elements
                    deselectAll();
                    riot.mount('label-panel', { id : parent.node.id, pointId : f_point.node.id })
                }
                f_point.selectize({
                    rotationPoint: false,
                    points: []
                });
                selectedElements.push(f_point);
                e.stopPropagation();
            });
        }

        //This is a workaround of dragend event. This way it stops firing click event
        function onMouse(shape, dragCB) {
            var mousestate = 0;
            shape.on('mousedown', function(e) {
                mousestate = 1;
                // TODO: Remove
                shape.on('mousemove', function(e) {
                    mousestate = 2;
                });
                shape.on('mouseup', function(e) {
                    if (mousestate === 2) {
                        dragCB && dragCB(e);
                    }
                    mousestate = 0;
                });
                e.stopPropagation();
            });
        }

        function deselectAll() {
            selectedElements.forEach(el => {
                el.selectize(false, {
                    deepSelect: true
                });
                el.selectize(false);
            });
            selectedElements = [];
        }

        function selectAll() {
            labellingData[imgSelected.name].shapes.forEach(shape => {
                let el = SVG.get(shape.id);
                el.selectize({
                    rotationPoint: false
                });
                selectedElements.push(el);
            });
        }

        // When create, move, resize, delete
        function attachShapeData(shape) { // update data with shape detail'
            var points = getPoints(shape);
            attachShapeToImg(shape.node.id, shape.type, shape.rbox(myCanvas), points);
        }

        function updateFeaturePoints(shape) {
            $("[for=" + shape.node.id + "]").each((i, pointEl) => {
                updateFeaturePointPosition(SVG.get($(pointEl).attr("id")));
            });
        }

        function updateFeaturePointPosition(pointEl) {
            updateFeaturePointInStore(pointEl.attr("for"), pointEl.id(), pointEl.rbox(myCanvas));
        }

        /**
         * Returns the feature points stored in the shape
         * @param {SVGElement} shape - shape SVGElement
         */
        function getPoints(shape) {
            var points;

            switch (shape.type) {
                case "rect":
                    var box = shape.rbox(myCanvas);
                    return [box.x, box.y, box.w, box.h];
                case "circle":
                    var box = shape.rbox(myCanvas);
                    return [box.cx, box.cy, shape.attr("r")];
                    /* case "ellipse":
                        var box = shape.rbox(myCanvas);
                        return [box.cx, box.cy, box.rx, box.ry]; */
                case "polygon":
                    //Polygon points are relative to it's container SVG
                    var parentSvg = $('#'+shape.node.id).closest('svg');
                    var calculatedPoints = [];
                    var vector = {
                        x: parseInt(parentSvg.attr("x"), 10) || 0,
                        y: parseInt(parentSvg.attr("y"), 10) || 0
                    }
                    shape.array().value.forEach(ponitArr => {
                        calculatedPoints.push([ponitArr[0] + vector.x, ponitArr[1] + vector.y]);
                    });
                    return calculatedPoints;
                    /* case "line":
                        console.log(shape.array())
                        points = [];
                        break;
                    case "path":
                        console.log(shape.array())
                        points = [];
                        break; */
            }
        }

        /**
         * Draws all the shapes and points in the currrently selected image
         */
        function drawOnCanvas() {
            for (var shapeIndex in labellingData[imgSelected.name].shapes) {
                var shape = labellingData[imgSelected.name].shapes[shapeIndex];
                drawShape(shape);
            }
        }


        /**
         * Draws a shape and all the feature points in the shape based on current image scale
         * @param {Object} shape - the shape data object as stored in labellingData
         */
        function drawShape(shape) {
            if (!shape) return;
            var scale = imgSelected.size.imageScale;
            var currentShape;
            switch (shape.type) {
                case "rect":
                    var rect = myCanvas.nested()
                        .rect(shape.points[2] * scale, shape.points[3] * scale)
                        .move(shape.points[0] * scale, shape.points[1] * scale)
                        .addClass('labelbox shape')
                        .id(shape.id)
                        .resize();
                    rect.parent().draggable();
                    //Add feature points
                    currentShape = rect;
                    break;
                case "circle":
                    var circle = myCanvas.nested()
                        .circle().radius(shape.points[2] * scale)
                        .attr("cx", shape.points[0] * scale)
                        .attr("cy", shape.points[1] * scale)
                        .addClass('labelcircle shape')
                        .id(shape.id)
                        .resize();
                    circle.parent().draggable();
                    //Add feature points
                    currentShape = circle;
                    break;
                    /* case "ellipse":
                        var ellipse = myCanvas.nested()
                            .ellipse().radius(shape.points[2], shape.points[3])
                            .move(shape.points[0],shape.points[1])
                            .addClass('labelellipse shape')
                            .resize();
                        ellipse.parent().draggable();
                        attachEvents(ellipse);
                        //Add feature points */
                case "polygon":
                    var poly = myCanvas.nested()
                        .polygon(scaleShapePoints(shape.points, scale, shape.type))
                        //.move(shape.points[0],shape.points[1])
                        .addClass('labelpolygon shape')
                        .id(shape.id)
                        .resize();
                    poly.parent().draggable();
                    //Add feature points
                    currentShape = poly;
                    break;
                default:
                    console.error('shape ' + shape.type + ' does not exist');
                    break;
            }
            // Sanity check, in case currentShape is empty
            if (currentShape) {
                moveOnlyOnMoveTool(currentShape);
                attachEvents(currentShape);
                attachShapeListener(currentShape);
                drawAllFeaturePoints(shape.featurePoints, currentShape);
            }
        }

        /**
         * Draws all the feature points in the shape according to current image scale and attachEventListeners to points
         * @param {Array} fPoints - array of shape featurePoints
         * @param {Object} parent - shape that contains the feature points
         * @see getPoints
         */
        function drawAllFeaturePoints(fPoints, parent) {
            let scaledFPoints = scaleFeaturePoints(fPoints, imgSelected.size.imageScale);
            for (var fPointIndex in scaledFPoints) {
                var fPoint = getPointToDraw(scaledFPoints[fPointIndex], parent, {
                    x: 0,
                    y: 0
                });
                fPoint.id(scaledFPoints[fPointIndex].id);
                attachEventsToFeaturePoint(fPoint, parent);
            }
        }
    </script>
</workarea>
